/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package databook.util;

import java.awt.geom.Rectangle2D;
import java.util.Iterator;

import prefuse.data.Node;
import prefuse.data.tuple.TupleSet;
import prefuse.visual.VisualItem;
import prefuse.visual.expression.VisiblePredicate;
import prefuse.action.layout.GridLayout;

/**
 *
 * @author zach
 */
public class SquareGridLayout extends GridLayout {
    private int buffer = 25;
    
    public SquareGridLayout(String group) {
        super(group);
    }
    
    public SquareGridLayout(String group,int buffer) {
        super(group);
        this.buffer = buffer;
    }
    
    /**
     * @see prefuse.action.Action#run(double)
     */
    public void run(double frac) {
        Rectangle2D b = getLayoutBounds();
        double bx = b.getMinX() + buffer, by = b.getMinY() + buffer;
        double w = b.getWidth() - (2* buffer), h = b.getHeight() - (2*buffer);
        
        TupleSet ts = m_vis.getGroup(m_group);
        try {
            ts.addColumn("JITTER_X",double.class);
            ts.addColumn("JITTER_Y",double.class);
            ts.addColumn("JITTER_SET",boolean.class);
        } catch (IllegalArgumentException iae) {}
        int m = rows, n = cols;
        if ( analyze ) {
            int[] d = analyzeGraphGrid(ts);
            m = d[0]; n = d[1];
        }
        
        Iterator iter = ts.tuples(VisiblePredicate.TRUE,new DatabookSort());
        // layout grid contents
        //for ( int i=0; iter.hasNext() && i < m*n; ++i ) {
        for (int r = 0; r < m; r++) {
            for (int c = 0; iter.hasNext() && c < n; c++) {
                if (r % 2 == 1) c = n-1-c;
                VisualItem item = (VisualItem)iter.next();
                item.setVisible(true);
                //System.out.println(item.getString("ID") + " " + item.getSize());
                if (!item.getBoolean("JITTER_SET")) {
                    item.setDouble("JITTER_X", jitter(w/n/2/item.getSize()));
                    item.setDouble("JITTER_Y", jitter(h/m/2/item.getSize()));
                    item.setBoolean("JITTER_SET",true);
                }
                double jitter_x = item.getDouble("JITTER_X");
                double jitter_y = item.getDouble("JITTER_Y");

                double x = bx + w*((c)/(double)(n-1)) + jitter_x;
                double y = by + h*((r)/(double)(m-1)) + jitter_y;
                setX(item,null,x);
                setY(item,null,y);
                if (r%2==1) c = -1 *(c-n+1);
            }
        }
        // set left-overs invisible
        while ( iter.hasNext() ) {
            VisualItem item = (VisualItem)iter.next();
            System.out.println("Setting " + item.getString("ID") + " to false");
            item.setVisible(false);
        }
    }
    
    public double jitter(double bounds) {
        double jitter = bounds*(Math.random() - .5);
        int sign = (int)Math.floor(Math.random());
        //System.out.println("Bounds: " + bounds + " Jitter: " + jitter + " Sign: " + sign);
        //return 0;
        if (sign == 0) return jitter;
        else return -1 * jitter;
        //else return -1 * bounds;
    }
    
    /**
     * Analyzes a set of nodes to try and determine grid dimensions. Currently
     * looks for the edge count on a node to drop to 2 to determine the end of
     * a row.
     * @param ts TupleSet ts a set of nodes to analyze. Contained tuples
     * <b>must</b> implement be Node instances.
     * @return a two-element int array with the row and column lengths
     */
    public static int[] analyzeGraphGrid(TupleSet ts) {
        // TODO: more robust grid analysis?
        int m;
        int count = 0;
        Iterator iter = ts.tuples(VisiblePredicate.TRUE);
        while (iter.hasNext()) {
            count++;
            iter.next();
        }
        m = (int)Math.ceil(Math.sqrt(count));
        if ((m * (m-1)) >= count)
            return new int[] {m,m-1};
        else 
            return new int[] {m,m};
    }

}
